package cn.mw.cmdb.task;

import cn.mw.cmdb.entity.AssetsDiscoverInfo;
import cn.mw.cmdb.entity.AssetsScanResultInfo;
import cn.mw.cmdb.mongoMapper.AssetsDiscoverMapper;
import cn.mw.cmdb.mongoMapper.AssetsScanResultMapper;
import cn.mw.cmdb.param.AssetsScanParam;
import cn.mw.cmdb.service.AssetsManageService;
import cn.mw.cmdb.service.impl.CmdbServiceManage;
import cn.mw.components.mongodb.tool.MongoUtils;
import cn.mw.microMonitorCommon.util.RedisUtils;
import cn.mw.microMonitorCommon.util.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Slf4j
public class AssetsDiscoveryMainTask implements Runnable{

    private Authentication authentication;

    private AssetsScanResultMapper detailMapper;

    private AssetsDiscoverMapper assetsDiscoverMapper;

    private List<AssetsScanParam> scanParamList;

    private Map<String, AssetsDiscoverInfo> assetsDiscoverInfoMap;

    private AssetsDiscoveryRunManage runManage;

    private RedisUtils redisUtils;

    private CmdbServiceManage cmdbServiceManage;

    private boolean scanType;

    public static final String LockKeyPrefix = "AssetsDiscovery:";

    public AssetsDiscoveryMainTask(Authentication authentication , AssetsScanResultMapper detailMapper , AssetsDiscoverMapper assetsDiscoverMapper
    , List<AssetsScanParam> scanParamList , Map<String, AssetsDiscoverInfo> assetsDiscoverInfoMap
    , AssetsDiscoveryRunManage runManage , RedisUtils redisUtils, boolean scanType, CmdbServiceManage cmdbServiceManage){
        this.authentication = authentication;
        this.detailMapper = detailMapper;
        this.assetsDiscoverMapper = assetsDiscoverMapper;
        this.scanParamList = scanParamList;
        this.assetsDiscoverInfoMap = assetsDiscoverInfoMap;
        this.runManage = runManage;
        this.redisUtils = redisUtils;
        this.scanType = scanType;
        this.cmdbServiceManage = cmdbServiceManage;
    }

    @Override
    public void run() {
        try {
            SecurityContextHolder.getContext().setAuthentication(authentication);
            doAssetsScanResults(authentication);
        }catch (Exception e){
            log.error("getAssetsScanResults" ,e);
        }finally {
            log.info("AssetsDiscoveryMainTask clear SecurityContextHolder {}" ,authentication.getPrincipal());
            SecurityContextHolder.clearContext();
        }
    }

    private void doAssetsScanResults(Authentication authentication) throws Exception {
        AssetsManageService assetsManageService = SpringUtils.getBean(AssetsManageService.class);

        int count = scanParamList.size();
        int threadNum = count;
        if(count > 5){
            threadNum = 5;
        }
        ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(threadNum);
        List<Future> futures = new ArrayList<>();

        for (AssetsScanParam scanParam : scanParamList) {
            String lockKey = LockKeyPrefix + scanParam.getTaskId();
            redisUtils.processWithRedisLock(lockKey, 15, TimeUnit.SECONDS, new Consumer<RedisUtils>() {
                @Override
                public void accept(RedisUtils redisUtils) {
                    try {
                        String scanRuleId = scanParam.getTaskId();
                        AssetsDiscoverInfo assetsDiscoverInfo = assetsDiscoverInfoMap.get(scanRuleId);
                        Integer nextBatchNo = (null == assetsDiscoverInfo.getMaxBatchNo())?1:assetsDiscoverInfo.getMaxBatchNo()+1;
                        log.info("Total scan :{}, current batchNo: {}, scan ID：{}", count, nextBatchNo, scanParam.getTaskId());
                        AssetsDiscoveryTask assetsDiscoveryTask = new AssetsDiscoveryTask(authentication ,nextBatchNo
                                ,assetsManageService ,runManage ,detailMapper ,assetsDiscoverMapper ,assetsDiscoverInfo ,scanParam, scanType,cmdbServiceManage);
                        Future future = threadPoolExecutor.submit(assetsDiscoveryTask);
                        futures.add(future);
                    }catch (Exception e){
                        log.error("discoverScan" ,e);
                    }
                }
            });
        }

        List<String> ids = new ArrayList<>();
        if(null != scanParamList){
            ids = scanParamList.stream().map(AssetsScanParam::getTaskId).collect(Collectors.toList());
        }
        try {
            for (Future future : futures) {
                future.get();
            }
        }catch (InterruptedException interruptedException){
            log.warn("doAssetsScanResults is interrupt :{}", ids);
        }finally {
            for(String id: ids){
                runManage.removeTask(id);
            }
            threadPoolExecutor.shutdown();
        }
        log.info("scan success, start upd asset state..");
    }

    private List<Map<String ,Integer>> getBatchNos(List<String> ruleIds){
        List<Map<String ,Integer>> batchNos = MongoUtils.getMaxFieldValue(detailMapper ,AssetsScanResultInfo.class
                ,MongoUtils.getFieldName(AssetsScanResultInfo::getScanRuleId) ,ruleIds
                ,MongoUtils.getFieldName(AssetsScanResultInfo::getBatchNo) ,Integer.class);
        return batchNos;
    }
}
